热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

更多|包裹_FlutterProvider状态管理

篇首语:本文由编程笔记#小编为大家整理,主要介绍了FlutterProvider状态管理相关的知识,希望对你有一定的参考价值。介绍、

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Flutter Provider状态管理相关的知识,希望对你有一定的参考价值。




介绍、类图分析、基本使用

Provider是一个由社区构建的状态管理包,而不是Google推出,但ProviderGoogle极力推荐的状态管理方式之一,它是对InheritedWidget组件进行了封装,使其更易用,更易复用。

学习本章节前,希望你能了解如下知识:


  • 熟悉dart语言
  • 熟悉flutter基本组件
  • 了解InheritedWidget
  • 了解ChangeNotifier

如果大家大家之前没接触过InheritedWidget,那么建议你先去了解,你可以通过链接来查看并掌握对应的只是 英文官方文档 中文官方文档 源码分析 视频教程


Provider优势

我们为什么要用Provider而不是直接使用InheritedWidget,我们看下官方介绍



  • 简化的资源分配与处置
  • 懒加载
  • 创建新类时减少大量的模板代码
  • 支持 DevTools
  • 更通用的调用 InheritedWidget 的方式(参考 Provider.of/Consumer/Selector)
  • 提升类的可扩展性,整体的监听架构时间复杂度以指数级增长(如 ChangeNotifier, 其复杂度为 O(N))


Provider类结构图


Provider类说明


Nested组件


  • Nested: 简化树结构嵌套过深
  • SingleChildWidget: 单个子组件的组件,但是它与ProxyWidget不同,有一个build方法。
  • SingleChildStatelessWidget: 它是一个实现SingleChildWidgetStatelessWidget,必须使用buildWithChild构建子组件
  • SingleChildStatefulWidget: 它是一个实现SingleChildWidgetStatefulWidget,是与Nested兼容的StatefulWidget

Provider组件

Provider组件分为四大类,分别如下:

Nested系列

MultiProvider: 主要作用是提高代码可读性和减少重复代码,是将多个提供者合并成单个线性的组件提供者。

SingleChildStatefulWidget系列


  • Selector0: 它是Selector至Selector6的基类
  • Selector1-6: 它们是将Selector0Provider.of结合使用的语法糖,继承自Selector0

SingleChildStatelessWidget系列


  • Consumer1-6: 消费者,只是调用了Provider.of,主要作用是从顶层获取Provider并将其值传递给了builder
  • InheritedProvider: InheritedWidget的通用实现,并且所有的继承自该类的都可以通过Provider.of来获取value
  • DeferredInheritedProvider: InheritedProvider的子类,用于监听流或者接收一个Future
  • StreamProvider: 监听流,并暴露出当前的最新值。
  • FutureProvider: 接收一个 Future,并在其进入 complete 状态时更新依赖它的组件。
  • ListenableProvider: 供可监听对象使用的特殊 provider,ListenableProvider 会监听对象,并在监听器被调用时更新依赖此对象的widgets
  • ChangeNotifierProvider: 为 ChangeNotifier 提供的 ListenableProvider规范,会在需要时自动调用 ChangeNotifier.dispose
  • ListenableProxyProvider0: 可见的代理提供者,主要是从其他提供者获取值。
  • ListenableProxyProvider1-6: 它是ListenableProvider的一个变体,继承ListenableProxyProvider0, 从其他提供者获取值
  • ChangeNotifierProxyProvider0: 主要用于构建和同步ChangeNotifierChangeNotifierProvider
  • Provider: 最基础的 provider 组成,接收一个任意值并暴露它。
  • ProxyProvider0: 它公开的值会通过创建或更新构建,然后传递给 InheritedProvider
  • ProxyProvider1-6: ProxyProvider0的语法糖。

InheritedContext系列


  • InheritedContext: 与InheritedProvider关联的BuildContext,提供公开的值
  • ReadContext: 公开读取方法
  • SelectContext: 在 BuildContext 上添加一个选择方法。
  • WatchContext: 公开 watch 方法。

Provider基本使用


第一步:添加依赖

本文中所有的代码都是基本空安全的&#xff0c;所有dart sdk版本为>&#61;2.12.0 <3.0.0&#xff0c;目前官方最新版本为^6.0.1

environment:
sdk: ">&#61;2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
provider: ^6.0.1

第二步&#xff1a;定义需要共享的数据

我们这里创建了一个类CountNotifier继承了ChangeNotifier&#xff0c;我们这里的案例是以计数器为案例&#xff0c;所有我们定义一个变量count&#xff0c;以及一个改变数值的increment方法&#xff0c;当我们调用increment后会对count进行&#43;1&#xff0c;最后调用notifyListeners()来更新数据&#xff0c;代码如下&#xff1a;

import &#39;package:flutter/material.dart&#39;;
class CountNotifier with ChangeNotifier
int count &#61; 0;
void increment()
count&#43;&#43;;
notifyListeners();


第三步&#xff1a;在应用程序入口初始化

我们在MaterialApp之前对定义的共享数据进行了初始化&#xff0c;代码如下&#xff1a;

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/provider_count_example/count_notifier.dart&#39;;
import &#39;package:flutter_provider_example/provider_count_example/provider_count_example.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
void main()
runApp(MyApp());
class MyApp extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return ChangeNotifierProvider(
create: (_) &#61;> CountNotifier(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ProviderCountExample(),
),
);


第四步&#xff1a;使用共享数据

我们定义了一个counter变量来获取到共享的数据&#xff0c;更新数据的方法直接通过counter.increment()&#xff0c;获取数据的方法通过ounter.count.toString()来获取&#xff0c;代码如下&#xff1a;

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/provider_count_example/count_notifier.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class ProviderCountExample extends StatefulWidget
&#64;override
_ProviderCountExampleState createState() &#61;> _ProviderCountExampleState();
class _ProviderCountExampleState extends State
&#64;override
Widget build(BuildContext context)
final counter &#61; Provider.of(context);
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
floatingActionButton: FloatingActionButton(
onPressed: ()
counter.increment();
,
child: Icon(Icons.add),
),
body: Center(
child: Text(counter.count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
),
);


总结

以上是对Provider进行了介绍、优势、类结构和说明以及一个基本使用的例子&#xff0c;相对于使用InheritedWidget来说&#xff0c;显然Provider使用起来更简单。但是从它的提供者、消费者这些类来看稍微复杂&#xff0c;后面的章节中我们来讲讲这些类。




八种提供者使用分析

前言

在我们上一篇文章中对Provider进行了介绍以及类结构的说明&#xff0c;最后还写了一个简单的示例&#xff0c;通过上一章节我们对Provider有了一个基本的了解&#xff0c;这一章节我们来说说Provider的8种提供者以及他们的使用区别。


Provider

Provider是最基本的Provider组件&#xff0c;可以使用它为组件树中的任何位置提供值&#xff0c;但是当该值更改的时候&#xff0c;它并不会更新UI&#xff0c;下面我们给出一个示例


第一步&#xff1a;创建模型

class UserModel
String name &#61; "Jimi";
void changeName()
name &#61; "hello";


第二步&#xff1a;应用程序入口设置


return Provider(
create: (_) &#61;> UserModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ProviderExample(),
),
);

第三步&#xff1a;使用共享数据

关于Consumer后面将消费者在提及&#xff0c;我们这里只需要知道有两个消费者&#xff0c;第一个用于展示模型的数据&#xff0c;第二个用于改变模型的数据。


  • 第一个Comsumer是用于读取模型的数据name
  • 第二个Consumer用于改变模型的数据name

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/provider_example/user_model.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class ProviderExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("ProviderExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (_, userModel, child)
return Text(userModel.name,
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
,
),
Consumer(
builder: (_, userModel, child)
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: ()
userModel.changeName();
,
child: Text("改变值"),
),
);
,
),
],
),
),
);


运行结果

我们点击按钮的会导致模型数据改变&#xff0c;但是模型数据改变之后UI并没有变化也没有重建&#xff0c;那是因为Provider提供者组件不会监听它提供的值的变化。



ChangeNotifierProvider

它跟Provider组件不同&#xff0c;ChangeNotifierProvider会监听模型对象的变化&#xff0c;而且当数据改变时&#xff0c;它也会重建Consumer&#xff08;消费者&#xff09;&#xff0c;下面我们给出一个示例


第一步&#xff1a;创建模型

细心点我们可以发现这里定义的模型有两处变化&#xff0c;如下&#xff1a;


  • 混入了ChangeNotifier
  • 调用了notifyListeners()

因为模型类使用了ChangeNotifier&#xff0c;那么我们就可以访问notifyListeners()并且在调用它的任何时候&#xff0c;ChangeNotifierProvider都会收到通知并且消费者将重建UI。

import &#39;package:flutter/material.dart&#39;;
class UserModel1 with ChangeNotifier
String name &#61; "Jimi";
void changeName()
name &#61; "hello";
notifyListeners();


第二步&#xff1a;应用程序入口设置

return ChangeNotifierProvider(
create: (_) &#61;> UserModel1(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ChangeNotifierProviderExample(),
),
);

第三步&#xff1a;使用共享数据

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_provider_example/user_model1.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class ChangeNotifierProviderExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("ChangeNotifierProvider"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (_, userModel, child)
return Text(userModel.name,
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
,
),
Consumer(
builder: (_, userModel, child)
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: ()
userModel.changeName();
,
child: Text("改变值"),
),
);
,
),
],
),
),
);


运行结果



FutureProvider

简单来说&#xff0c;FutureProvider用于提供在组件树中准备好使用其值时可能尚未准备好的值&#xff0c;主要是确保空值不会传递给任何子组件&#xff0c;而且FutureProvider有一个初始值&#xff0c;子组件可以使用该Future值并告诉子组件使用新的值来进行重建。

注意&#xff1a;


  • FutureProvider只会重建一次
  • 默认显示初始值
  • 然后显示Future
  • 最后不会再次重建

第一步&#xff1a;创建模型

这里和Provider不同的是增加了构造函数&#xff0c;以及changeName变成了Future&#xff0c;我们模拟网络请求延迟两秒后改变其值。

class UserModel2
UserModel2(this.name);
String? name &#61; "Jimi";
Future changeName() async
await Future.delayed(Duration(milliseconds: 2000));
name &#61; "hello";


第二步&#xff1a;提供Future

我们有一个方法&#xff0c;就是异步获取userModel2&#xff0c;模拟网络请求延迟两秒执行&#xff0c;最后修改了name并返回UserModel2


import &#39;package:flutter_provider_example/future_provider_example/user_model2.dart&#39;;
class UserFuture
Future asyncGetUserModel2() async
await Future.delayed(Duration(milliseconds: 2000));
return UserModel2(name: "获取新的数据");


第三步&#xff1a;应用程序入口设置

initialData是默认值&#xff0c;create参数我们传了一个Future&#xff0c;因为它接收的模型Create?>

return FutureProvider(
initialData: UserModel2(name: "hello"),
create: (_) &#61;> UserFuture().asyncGetUserModel2(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: FutureProviderExample(),
),
);

第四步&#xff1a;使用共享数据

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/future_provider_example/user_model2.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class FutureProviderExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("FutureProviderExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (_, userModel, child)
return Text(userModel.name ?? "",
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
,
),
Consumer(
builder: (_, userModel, child)
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: ()
userModel.changeName();
,
child: Text("改变值"),
),
);
,
),
],
),
),
);


运行结果

我们可以看到先展示默认值hello&#xff0c;最后获取到结果的时候展示了获取新的数据&#xff0c;我们尝试改变其值&#xff0c;虽然值改变但是并没有刷新UI。



StreamProvider

StreamProvider提供流值&#xff0c;是围绕StreamBuilder&#xff0c;所提供的值会在传入的时候替换掉新值。和FutureProvider一样&#xff0c;主要的区别在于值会根据多次触发重新构建UI。

如果你对StreamBuilder不太了解的话&#xff0c;那么你就很难理解StreamProvider&#xff0c;StreamProvider文档地址


第一步&#xff1a;创建模型

class UserModel3
UserModel3(this.name);
String? name &#61; "Jimi";
void changeName()
name &#61; "hello";


第二步&#xff1a;提供Stream

下面这段代码类似计时器&#xff0c;每隔一秒钟生成一个数字


import &#39;package:flutter_provider_example/stream_provider_example/user_model3.dart&#39;;
class UserStream
Stream getStreamUserModel()
return Stream.periodic(Duration(milliseconds: 1000),
(value) &#61;> UserModel3(name: "$value")
).take(10);


第三步&#xff1a;应用程序入口设置

这里也有initialData初始值&#xff0c;和FutureProvider类似&#xff0c;只是create属性是获取一个Stream流。


return StreamProvider(
initialData: UserModel3(name: "hello"),
create: (_) &#61;> UserStream().getStreamUserModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: StreamProviderExample(),
),
);

第四步&#xff1a;使用共享数据


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/stream_provider_example/user_model3.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class StreamProviderExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("StreamProviderExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (_, userModel, child)
return Text(userModel.name ?? "",
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
,
),
Consumer(
builder: (_, userModel, child)
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: ()
userModel.changeName();
,
child: Text("改变值"),
),
);
,
),
],
),
),
);


运行结果


MultiProvider

在上面的例子中我们都只是返回了一个提供者&#xff0c;在实际开发过程中肯定会有多个提供者&#xff0c;我们虽然可以采用嵌套的方式来解决&#xff0c;但是这样无疑是混乱的&#xff0c;可读性级差。这个时候强大的MultiProvder就产生了&#xff0c;我们来看下示例:


第一步&#xff1a;创建两个模型

import &#39;package:flutter/material.dart&#39;;
class UserModel1 with ChangeNotifier
String name &#61; "Jimi";
void changeName()
name &#61; "hello";
notifyListeners();

class UserModel4 with ChangeNotifier
String name &#61; "Jimi";
int age &#61; 18;
void changeName()
name &#61; "hello";
age &#61; 20;
notifyListeners();


第二步&#xff1a;应用程序入口设置

相对于方式一这种嵌套方式设置&#xff0c;方式二就显得尤为简单。

方式一&#xff1a;嵌套设置

return ChangeNotifierProvider(
create: (_) &#61;> UserModel1(),
child: ChangeNotifierProvider(
create: (_) &#61;> UserModel4(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiProviderExample(),
),
),
);

方式二&#xff1a;使用MultiProvider

return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) &#61;> UserModel1()),
ChangeNotifierProvider(create: (_) &#61;> UserModel4()),
/// 添加更多
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiProviderExample(),
),
);

第三步&#xff1a;使用共享数据


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_provider_example/user_model1.dart&#39;;
import &#39;package:flutter_provider_example/multi_provider_example/user_model4.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class MultiProviderExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("MultiProviderExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (_, userModel, child)
return Text(userModel.name,
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
,
),
Consumer(
builder: (_, userModel, child)
return Text(userModel.age.toString(),
style: TextStyle(
color: Colors.green,
fontSize: 30
)
);
,
),
Consumer2(
builder: (_, userModel1, userModel4, child)
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: ()
userModel1.changeName();
userModel4.changeName();
,
child: Text("改变值"),
),
);
,
),
],
),
),
);


运行结果



ProxyProvider

当我们有多个模型的时候&#xff0c;会有模型依赖另一个模型的情况&#xff0c;在这种情况下&#xff0c;我们可以使用ProxyProvider从另一个提供者获取值&#xff0c;然后将其注入到另一个提供者中。我们来看下代码演示


第一步&#xff1a;创建两个模型

下面我们创建了两个模型UserModel5WalletModel&#xff0c;而WalletModel依赖与UserModel5&#xff0c;当调用WalletModelchangeName方法时会改变UserModel5里面的name&#xff0c;当然我们在实际开发的过程中并不是这么简单&#xff0c;这里只是演示模型依赖时如果使用ProxyProvider


import &#39;package:flutter/material.dart&#39;;
class UserModel5 with ChangeNotifier
String name &#61; "Jimi";
void changeName(required String newName)
name &#61; newName;
notifyListeners();

class WalletModel
UserModel5? userModel5;
WalletModel(this.userModel5);
void changeName()
userModel5?.changeName(newName: "JIMI");


第二步&#xff1a;应用程序入口设置


return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) &#61;> UserModel5()),
ProxyProvider(
update: (_, userModel5, walletModel) &#61;> WalletModel(userModel5: userModel5),
)
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ProxyProviderExample(),
),
);

第三步&#xff1a;使用共享数据

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/proxy_provider_example/user_model5.dart&#39;;
import &#39;package:flutter_provider_example/proxy_provider_example/wallet_model.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class ProxyProviderExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("ProxyProviderExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (_, userModel, child)
return Text(userModel.name,
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
,
),
Consumer(
builder: (_, userModel, child)
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: ()
userModel.changeName(newName: "hello");
,
child: Text("改变值"),
),
);
,
),
Consumer(
builder: (_, walletModel, child)
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: ()
walletModel.changeName();
,
child: Text("通过代理改变值"),
),
);
,
),
],
),
),
);


运行结果



ChangeNotifierProxyProvider

ProxyProvider原理一样&#xff0c;唯一的区别在于它构建和同步ChangeNotifierChangeNotifierProvider&#xff0c;当提供者数据变化时&#xff0c;将会重构UI。

下面我们给出一个例子&#xff1a;


  • 获取书籍列表
  • 获取收藏书籍列表
  • 点击书籍可加入或者取消收藏
  • 通过代理实时重构UI

第一步&#xff1a;创建两个模型

1、BookModel

BookModel用户存储模型数据&#xff0c;将书籍转换成模型。


class BookModel

static var _books &#61; [
Book(1, "夜的命名数"),
Book(2, "大奉打更人"),
Book(3, "星门"),
Book(4, "大魏读书人"),
Book(5, "我师兄实在太稳健了"),
Book(6, "深空彼岸"),
];
// 获取书籍长度
int get length &#61;> _books.length;
// 根据ID获取书籍
Book getById(int id) &#61;> _books[id -1];
// 根据索引获取数据
Book getByPosition(int position) &#61;> _books[position];
// 更多....
class Book
final int bookId;
final String bookName;

Book(this.bookId, this.bookName);

2、BookManagerModel

BookManagerModel主要用于管理书籍、收藏书籍、取消收藏等操作


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart&#39;;
class BookManagerModel with ChangeNotifier
// 依赖bookModel
final BookModel _bookModel;
// 获取数据所有的ID
List? _bookIds;
// 构造函数
BookManagerModel(this._bookModel, BookManagerModel? bookManagerModel)
: _bookIds &#61; bookManagerModel?._bookIds ?? [];
// 获取所有的书
List get books &#61;> _bookIds!.map((id) &#61;> _bookModel.getById(id)).toList();
// 根据索引获取数据
Book getByPosition(int position) &#61;> books[position];
// 获取书籍的长度
int get length &#61;> _bookIds?.length ?? 0;
// 添加书籍
void addFaves(Book book)
_bookIds!.add(book.bookId);
notifyListeners();

// 删除书籍
void removeFaves(Book book)
_bookIds!.remove(book.bookId);
notifyListeners();


第二步&#xff1a;应用程序入口设置

return MultiProvider(
providers: [
Provider(create: (_) &#61;> BookModel()),
ChangeNotifierProxyProvider(
create: (_) &#61;> BookManagerModel(BookModel()),
update: (_, bookModel, bookManagerModel) &#61;> BookManagerModel(bookModel),
)
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ChangeNotifierProxyProviderExample(),
),
);

第三步&#xff1a;设置BottomNavigationBar

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_a.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_b.dart&#39;;
class ChangeNotifierProxyProviderExample extends StatefulWidget
&#64;override
_ChangeNotifierProxyProviderExampleState createState() &#61;> _ChangeNotifierProxyProviderExampleState();
class _ChangeNotifierProxyProviderExampleState extends State
var _selectedIndex &#61; 0;
var _pages &#61; [PageA(), PageB()];
&#64;override
Widget build(BuildContext context)
return Scaffold(
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index)
setState(()
_selectedIndex &#61; index;
);
,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.book),
label: "书籍列表"
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: "收藏"
)
],
),
);


第四步&#xff1a;书籍列表UI构建

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class PageA extends StatelessWidget
&#64;override
Widget build(BuildContext context)

var bookModel &#61; Provider.of(context);

return Scaffold(
appBar: AppBar(
title: Text("书籍列表"),
),
body: ListView.builder(
itemCount: bookModel.length,
itemBuilder: (_, index) &#61;> BookItem(id: index &#43; 1),
),
);


第五步&#xff1a;收藏列表UI构建

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class PageB extends StatelessWidget
&#64;override
Widget build(BuildContext context)
var bookManagerModel &#61; Provider.of(context);
var bookCount &#61; bookManagerModel.length;
return Scaffold(
appBar: AppBar(
title: Text("收藏列表"),
),
body: ListView.builder(
itemCount: bookCount,
itemBuilder: (_, index) &#61;> BookItem(id: bookManagerModel.getByPosition(index).bookId),
),
);


其他辅助封装类


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class BookButton extends StatelessWidget

final Book book;

BookButton(
Key? key,
required this.book
) : super(key: key);

&#64;override
Widget build(BuildContext context)

var bookManagerModel &#61; Provider.of(context);

return GestureDetector(
onTap: bookManagerModel.books.contains(this.book)
? () &#61;> bookManagerModel.removeFaves(this.book)
: () &#61;> bookManagerModel.addFaves(this.book),
child: SizedBox(
width: 100,
height: 60,
child: bookManagerModel.books.contains(this.book)
? Icon(Icons.star, color: Colors.red,)
: Icon(Icons.star_border),
),
);


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart&#39;;
import &#39;package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_button.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class BookItem extends StatelessWidget
final int id;
BookItem(
Key? key,
required this.id
) : super(key: key);
&#64;override
Widget build(BuildContext context)
var bookModel &#61; Provider.of(context);
var book &#61; bookModel.getById(id);
return ListTile(
leading: CircleAvatar(
child: Text("$book.bookId"),
),
title: Text("$book.bookName",
style: TextStyle(
color: Colors.black87
),
),
trailing: BookButton(book: book),
);


运行结果


ListenableProxyProvider

ListenableProxyProviderListenableProvider的一个变体&#xff0c;但是在使用上和ChangeNotifierProvider效果惊人的一致&#xff0c;如果大家对ListenableProxyProvider有更深的理解&#xff0c;请联系我补充。


总结

Provider为我们提供了非常多的提供者&#xff0c;总共有八种。但我们比较常用的是ChangeNotifierProviderMultiProviderChangeNotifierProxyProvider&#xff0c;关于其他的提供者可根据自己的实际应用场景来。




四种消费者使用分析

前言

在上一篇文章中我们对Provider的8种提供者进行了详细的描述以及用对应的案例说明他们的区别&#xff0c;那么这一节我们来聊一聊Provider的消费者&#xff0c;如果去优化你的项目结构以及它们的使用区别。


Provider.of

Provider.of(context)Provider为我们提供的静态方法&#xff0c;当我们使用该方法去获取值的时候会返回查找到的最近的T类型的provider给我们&#xff0c;而且也不会遍历整个组件树&#xff0c;下面我们看下代码&#xff1a;


第一步&#xff1a;定义模型

我们定义了一个CountNotifier1的模型&#xff0c;后面所有的示例代码将围绕该模型来演示


import &#39;package:flutter/material.dart&#39;;
class CountNotifier1 with ChangeNotifier
int count &#61; 0;
void increment()
count&#43;&#43;;
notifyListeners();


第二步&#xff1a;应用程序入口设置

return ChangeNotifierProvider(
create: (_) &#61;> CountNotifier1(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ConsumerExample(),
),
);

第三步&#xff1a;使用Provider.of

这里读取值和点击按钮&#43;1时都是通过Provider.of()来获取及使用。

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/consumer_example/count_notifier1.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class ConsumerExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("ConsumerExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(Provider.of(context).count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
Padding(
padding: EdgeInsets.only(
top: 20
),
child: ElevatedButton(
onPressed: ()
Provider.of(context).increment();
,
child: Text("点击加1"),
),
)
],
),
),
);


错误日志

当我们运行代码的时候会提示一个报错&#xff0c;它提示说试图从Widget树外部监听提供者公开的值&#xff0c;如果要修复可以把listen改成false&#xff0c;这个问题其实是在Provider 4.0.2后会出现的&#xff0c;最主要的是它的默认行为就是ture&#xff0c;错误日志如下&#xff1a;


&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61; Exception caught by gesture &#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
The following assertion was thrown while handling a gesture:
Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button&#39;s onPressed) that called
Provider.of without passing &#96;listen: false&#96;.
To fix, write:
Provider.of(context, listen: false);
It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn&#39;t care about the value.
The context used was: ConsumerExample(dependencies: [_InheritedProviderScope])
&#39;package:provider/src/provider.dart&#39;:
Failed assertion: line 276 pos 7: &#39;context.owner!.debugBuilding ||
listen &#61;&#61; false ||
debugIsInInheritedProviderUpdate&#39;
When the exception was thrown, this was the stack:
........
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;

设置listen为false

Provider.of(context, listen: false).increment();

运行结果



Consumer

Consumber只是在Widget中调用了Prvoider.of&#xff0c;并将其构造实现委托给了构造器&#xff0c;比如我们常见的Builder&#xff0c;如果你的Widget依赖多个模型&#xff0c;那么它还提供了Consumer23456方便调用&#xff0c;我们接下来对上面的案例采用Consumer来修改


用Consumer包裹组件

里面有个builder构造器&#xff0c;当我们把body改成下面重新运行后可以发现和使用Provider.of的结果一样&#xff0c;但是这里不需要在像使用Provider.of那样每次使用都要写一大串的重复性代码。

里面有三个属性&#xff1a;


  • context: 当前的上下文
  • Provider.of(context): 模型对象
  • child: 子组件(不需要刷新的部分)

body: Consumer(
builder: (_, CountNotifier1 countNotifier1, child)
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(countNotifier1.count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
Padding(
padding: EdgeInsets.only(
top: 20
),
child: ElevatedButton(
onPressed: ()
countNotifier1.increment();
,
child: Text("点击加1"),
),
)
],
),
);
,
),

优化Consumer

优化方式一&#xff1a;尽可能调整Consumer的位置

我们在上面的代码中发现Center以及Column组件也被Consumer包裹了进来&#xff0c;但是这两个组件是不需要更新状态的&#xff0c;而我们每次构建的Widget的时候&#xff0c;会重建整个body&#xff0c;所以我们优化一下代码结构&#xff0c;看起来就像下面这样&#xff1a;


body: Center(
child: Consumer(
builder: (_, CountNotifier1 countNotifier1, child)
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
countNotifier1.count.toString(),
style: TextStyle(color: Colors.red, fontSize: 50),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: ()
countNotifier1.increment();
,
child: Text("点击加1"),
),
),
Container(
child: Column(
children: [
Text("更多组件1"),
Text("更多组件2"),
Text("更多组件3"),
Text("更多组件4"),
Text("更多组件5"),
Text("更多组件6"),
],
),
)
],
),
);
,
)
)

优化方式二&#xff1a;不需要刷新但被Consumer包裹的组件用child

比如上面我们有更多组件1-6甚至数百个组件无需刷新状态&#xff0c;但由于你用Consumer包裹会导致全部刷新&#xff0c;那么明显会导致性能的下降&#xff0c;你可能会想到单独用多个Consumer包裹需要刷新的组件就解决了&#xff0c;但这不就是本末倒置了吗&#xff0c;本身Provider是解决代码的健壮重复的代码&#xff0c;所以这个时候我们可以采用Consumer为我们提供的child参数&#xff0c;如下&#xff1a;


body: Center(
child: Consumer(
builder: (_, CountNotifier1 countNotifier1, child)
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
countNotifier1.count.toString(),
style: TextStyle(color: Colors.red, fontSize: 50),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: ()
countNotifier1.increment();
,
child: Text("点击加1"),
),
),
child!
],
),
);
,
child: Container(
child: Column(
children: [
Text("更多组件1"),
Text("更多组件2"),
Text("更多组件3"),
Text("更多组件4"),
Text("更多组件5"),
Text("更多组件6"),
],
),
),
)
),

Selector

Selector类和Consumer类似&#xff0c;只是对build调用Widget方法时提供更精细的控制&#xff0c;简单点来说&#xff0c;Selector也是一个消费者&#xff0c;它允许你可以从模型中准备定义哪些属性。

我们来举个例子&#xff1a;

比如&#xff0c;用户模型中有50个属性&#xff0c;但是我只需要更新年龄&#xff0c;这样我希望不需要重建用户名电话号码等组件&#xff0c;那么Selector就是用于解决这个问题&#xff0c;我们看一下示例&#xff1a;


第一步&#xff1a;定义模型


import &#39;package:flutter/material.dart&#39;;
class UserModel6 with ChangeNotifier
String name &#61; "Jimi";
int age &#61; 18;
String phone &#61; "18888888888";
void increaseAge()
age&#43;&#43;;
notifyListeners();


第二步&#xff1a;应用程序入口设置


return ChangeNotifierProvider(
create: (_) &#61;> UserModel6(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: SelectorExample(),
),
);

第三步&#xff1a;使用Selector更精细的控制


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/selector_example/user_model6.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class SelectorExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("SelectorExample"),
),
body: Center(
child: Selector(
selector: (_, userModel6) &#61;> userModel6.age,
builder: (_, age, child)
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(age.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 30
)
),
child!
],
);
,
child: Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: ()
Provider.of(context, listen: false).increaseAge();
,
child: Text("改变年龄"),
),
),
),
),
);


运行结果



InheritedContext

InheritedContextProvider内置扩展了BuildContext&#xff0c;它不保存了组件在树中自己位置的引用&#xff0c;我们在上面的案例中见到Provider.of(context&#xff0c;listen: false)&#xff0c;其实这个of方法就是使用Flutter查找树并找到Provider子类型为CountNotifier1而已。


三大方式&#xff1a;


  • BuildContext.read: BuildContext.read()可以替换掉Provider.of(context&#xff0c;listen: false)&#xff0c;它会找到CountNotifier1并返回它。
  • BuildContext.watch: BuildContext.watch()可以替换掉Provider.of(context&#xff0c;listen: false)&#xff0c;看起来和read没有什么不同&#xff0c;但是使用watch你就不需要在使用Consumer
  • BuildContext.select: BuildContext.select()可以替换掉Provider.of(context&#xff0c;listen: false)&#xff0c;看起来和watch也没有什么不同&#xff0c;但是使用select你就不需要在使用Selector

BuildContext.read

下面两种使用方式结果是一样的

使用Provider.of()


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/Inherited_context_example/count_notifier2.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class InheritedContextExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("InheritedContextExample"),
),
/// Provider.of 获取值
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(Provider.of(context).count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () &#61;> Provider.of(context, listen: false).increment(),
child: Text("点击加1"),
),
),
],
),
),
);

使用BuildContext.read

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/Inherited_context_example/count_notifier2.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class InheritedContextExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("InheritedContextExample"),
),
/// read 获取值
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(context.read().count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () &#61;> Provider.of(context, listen: false).increment(),
child: Text("点击加1"),
),
),
],
),
),
);


BuildContext.watch

使用Consumer

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/Inherited_context_example/count_notifier2.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class InheritedContextExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("InheritedContextExample"),
),
/// Consumer 获取值
body: Center(
child: Consumer(
builder: (_, countNotifier2, child)
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(countNotifier2.count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () &#61;> countNotifier2.increment(),
child: Text("点击加1"),
),
),
],
);
,
),
),
);

使用BuildContext.watch

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/Inherited_context_example/count_notifier2.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class InheritedContextExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
/// 重要
final countNotifier2 &#61; context.watch();
return Scaffold(
appBar: AppBar(
title: Text("InheritedContextExample"),
),
/// watch
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(countNotifier2.count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () &#61;> countNotifier2.increment(),
child: Text("点击加1"),
),
),
],
),
),
);


BuildContext.select

使用Selector


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/Inherited_context_example/count_notifier2.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class InheritedContextExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("InheritedContextExample"),
),
/// Selector
body: Center(
child: Selector(
selector: (_, countNotifier2) &#61;> countNotifier2.count,
builder: (_, count, child)
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
child!
],
);
,
child: Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () &#61;> Provider.of(context, listen: false).increment(),
child: Text("点击加1"),
),
),
),
),
);

使用BuildContext.select


import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter_provider_example/Inherited_context_example/count_notifier2.dart&#39;;
import &#39;package:provider/provider.dart&#39;;
class InheritedContextExample extends StatelessWidget
&#64;override
Widget build(BuildContext context)

/// 重要
final count &#61; context.select((CountNotifier2 countNotifier2) &#61;> countNotifier2.count);
return Scaffold(
appBar: AppBar(
title: Text("InheritedContextExample"),
),
/// select
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () &#61;> Provider.of(context, listen: false).increment(),
child: Text("点击加1"),
),
)
],
),
),
);


总结

Flutter为我们提供了多种读取值的方式&#xff0c;上面我们对消费者四大类的一个使用和分析对比&#xff0c;大家可根据自己的实际应用场景去使用对应的方式。



推荐阅读
author-avatar
xkxk22
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有